package junior.db.query;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiModelProperty;
import junior.db.dao.base.Sort;
import junior.util.reflect.ReflectUtils;
import junior.util.value.ValueUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author wubo
 * @date 2024/9/3 11:59
 */
public class QueryArgs {
    
    /**
     * 过滤
     */
    @ApiModelProperty(value = "过滤列表", dataType = "Map<String, Object>")
    private Map<String, Object> filters = Maps.newHashMap();
    /**
     * 排序
     */
    @ApiModelProperty("排序列表")
    private List<Sort> sorts = Lists.newArrayList();
    /**
     * 指定查询字段
     */
    @ApiModelProperty(hidden = true)
    private String[] selectFields;
    
    public <T extends QueryArgs> T withSelectFields(String... selectFields) {
        setSelectFields(selectFields);
        return (T) this;
    }
    
    public void setSelectFields(String[] selectFields) {
        if (selectFields != null) {
            for (String selectField : selectFields) {
                QueryHelper.checkField(selectField);
            }
        }
        this.selectFields = selectFields;
    }
    
    public String[] getSelectFields() {
        return selectFields;
    }
    
    public Map<String, Object> getFilters() {
        return filters;
    }
    
    public void setFilters(Map<String, Object> filters) {
        if (filters != null) {
            for (String key : filters.keySet()) {
                with(key, filters.get(key));
            }
        }
    }
    
    public <T extends QueryArgs> T with(String key, Object value) {
        QueryHelper.checkField(key);
        filters.put(key, value);
        return (T) this;
    }
    
    public boolean hasFilter(String key) {
        return filters.containsKey(key);
    }
    
    public List<Sort> getSorts() {
        return sorts;
    }
    
    public void setSorts(List<Sort> sorts) {
        if (sorts != null) {
            for (Sort sort : sorts) {
                withSort(sort);
            }
        }
    }
    
    public <T extends QueryArgs> T withSort(Sort sort) {
        Assert.notNull(sort, "Parameter sort is required");
        Assert.notNull(sort.getField(), "Parameter sort.field is required");
        if (sorts == null) {
            sorts = Lists.newArrayList();
        }
        QueryHelper.checkField(sort.getField());
        sorts.add(sort);
        return (T) this;
    }
    
    public final void checkSelectFields(Class<?> voClass) {
        if (selectFields == null) return;
        
        Field[] fields = ReflectUtils.getDeclaredFields(voClass, null, true, false);
        
        List<String> fieldList = Lists.newArrayListWithExpectedSize(fields.length);
        for (Field f : fields) {
            if (f.getType().isArray()) {
                continue;
            }
            if (Collection.class.isAssignableFrom(f.getType())) {
                continue;
            }
            fieldList.add(f.getName());
        }
        
        if (! fieldList.containsAll(Arrays.asList(selectFields))) {
            throw new IllegalArgumentException("Select fields is illegal");
        }
    }
    
    public final void checkFilterFields(String... fields) {
        if (getFilters().isEmpty()) return;
        
        Assert.notEmpty(fields, "Parameter fields is required");
        
        List<String> fieldList = Arrays.asList(fields);
        if (! fieldList.containsAll(getFilters().keySet())) {
            throw new IllegalArgumentException("Filter fields is illegal");
        }
    }
    
    public final void checkSortFields(String... fields) {
        List<Sort> sorts = getSorts();
        if (sorts == null || sorts.isEmpty()) return;
        
        Assert.notEmpty(fields, "Parameter fields is required");
        
        List<String> fieldList = Arrays.asList(fields);
        
        for (Sort sort : sorts) {
            if (! fieldList.contains(sort.getField())) {
                throw new IllegalArgumentException("Sort field "+sort.getField()+" is illegal");
            }
        }
    }
    
    public final void requiredFilterFields(String... fields) {
        Assert.notEmpty(fields, "Parameter fields is required");
        
        List<String> fieldList = Arrays.asList(fields);
        Collection<String> notPresents = CollectionUtils.removeAll(fieldList, getFilters().keySet());
        
        if (! notPresents.isEmpty()) {
            throw new IllegalArgumentException("Filter fields is required: " + notPresents);
        }
    }
    
    public Object get(String key) {
        return filters.get(key);
    }
    
    public <T> T get(String key, Class<T> typeClass) {
        Object value = filters.get(key);
        if (value == null) return null;
        return ValueUtils.convert(value, typeClass);
    }
    
}
